package handling;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 免责声明：本模拟器源代码下载自ragezone.com，仅用于技术研究学习，无任何商业行为。
 */
public class WorkerQueue {

    private static final Logger log = LogManager.getLogger(WorkerQueue.class.getName());
    private static int MAX_THREAD_COUNT = 20;
    private final AtomicInteger atomicInteger = new AtomicInteger(1);
    private String name;
    private List<HandlerWorker> queues = Collections.synchronizedList(new ArrayList<>());

    public WorkerQueue(String name) {
        this.name = name;
        initThreads(MAX_THREAD_COUNT);
    }

    public WorkerQueue(final String name, int num) {
        this.name = name;
        initThreads(MAX_THREAD_COUNT <= 0 ? MAX_THREAD_COUNT : 1);

    }

    private void initThreads(final int num) {
        for (int i = 0; i < num; i++) {
            HandlerWorker worker = new HandlerWorker(name + "-T-" + atomicInteger.getAndIncrement());
            worker.start();
            queues.add(worker);
        }
    }

    public final void runThread(int n, final Runnable runnable) {
        HandlerWorker worker = queues.get(n % queues.size());
        if (worker != null) {
            worker.addRunnable(runnable);
            return;
        }
        log.error("worker is null");
    }

    final class HandlerWorker extends Thread {

        private final LinkedList<Runnable> runnables;
        private volatile boolean running;

        public HandlerWorker(@NotNull String name) {
            super(name);
            this.running = true;
            this.runnables = new LinkedList<>();
        }

        public final void addRunnable(final Runnable runnable) {
            synchronized (runnables) {
                runnables.addLast(runnable);
                runnables.notifyAll();
            }
        }

        @Override
        public void run() {
            while (running) {
                synchronized (runnables) {
                    while (runnables.isEmpty()) {
                        try {
                            runnables.wait();
                        } catch (InterruptedException ex) {
                            log.error("", ex);
                        }
                    }
                    runnables.removeFirst().run();
                }
            }
        }
    }
}
